Our
first task is to take a BizTalk orchestration workflow and expose one
of its ports as a WCF-enabled web service. Fortunately for us, this is
a fairly straightforward undertaking that requires no actual coding.
Setting up the project
The
use case we will use throughout this chapter involves the ordering of
pharmaceutical products. Our initial assignment is to define the shape
of the data representing a "new order". I've built a schema named NewOrder_XML.xsd with a root node name of NewOrder
and a structure which holds the characteristics of the order, the
particular items that made up the order, and the corresponding sales
territory information. The namespace of my schema, http://Seroter.BizTalkSOA
.Chapter3.OrderManagement.BizTalk/Contract, will surface again once the service WSDL is generated.
Now
that we have a contract definition representing a new order, we
assemble an orchestration workflow that consumes this data entity.
Recall from Chapter 1 that a BizTalk orchestration depends on messages
which equate to the data being sent and received by the orchestration.
These messages are immutable so their data cannot be manipulated by the
orchestration except during message creation. For our first simple
orchestration, we have a message representing the inbound request, and
a separate message for the acknowledgement returned to the caller. In
this case, both messages are of the same schema type.
Once
our messages have been defined, we sketch out the orchestration's
sequential flow. In this case, let's do a very simple orchestration
that receives the new order and then constructs and return response
message with an altered Status value.
Finally,
I set up a public, request-response logical port, which acts as the
interface and entry point to the orchestration. This port has a single
operation named. SubmitNewOrder
The final orchestration looks like this:
Notice
that absolutely nothing we've done within this orchestration hints at
its role as the realization of a WCF service. Ideally, an
orchestration's ports are not tightly coupled to a runtime transport
scheme, which in return means greater flexibility and reuse. The
orchestration port only dictates a message type and message exchange
pattern while remaining transport neutral. Before continuing on, I went
ahead and built and deployed this project to ensure that my BizTalk
application was created and refreshed.
Generating the WCF endpoint
Unlike
most BizTalk receive adapters, which poll or listens to existing URIs
(such as file locations, databases, queues), the WCF adapters require
us to create new endpoints that BizTalk uses to get data absorbed into
the system. Fortunately, Microsoft makes generating such endpoints
fairly easy by introducing the BizTalk WCF Service Publishing Wizard.
Pitfall
Unlike the classic ASMX-based BizTalk Web Services Publishing Wizard, the BizTalk project in Visual Studio.NET does NOT have to be deployed in the Global Assembly Cache in order for the BizTalk WCF Service Publishing Wizard
to locate it. However, if the BizTalk project assembly had been
deployed at any time previously, then, the freshly updated assembly
must be deployed to the GAC so that the Visual Studio wizard pulls the
latest version of the assembly.
While one can launch the BizTalk WCF Service Publishing Wizard from the Microsoft BizTalk Server 2009 folder on the Start
menu, it's much simpler to trigger this wizard from within Visual
Studio.NET itself. We initiate this wizard by going to the Visual
Studio.NET Tools menu and choosing BizTalk WCF Service Publishing Wizard.
What we see next is a new window that will walk us through all the
steps necessary to generate the service and endpoint we desire.
The first thing that this wizard needs to know is what type of WCF service we'd like to produce. Our choices are:
Endpoint type
|
Usage scenario
|
---|
Service Endpoint
|
If you choose this type of endpoint, then this wizard will be
responsible for generating a consumable WCF service that is hosted
outside of BizTalk Server itself. Only WCF adapters that reside in the
BizTalk Isolated Host (WCF-WSHttp, WCF-BasicHttp, and
WCF-CustomIsolated) can be chosen here. Notice that you are able to
allow metadata browsing of this service (through a WCF behavior) by
selecting the Enable metadata endpoint
checkbox. If you'd like the wizard to automatically create the BizTalk
receive port/location which is linked to this freshly generated WCF
service, then you may also select the corresponding checkbox that
performs this action. |
Metadata only endpoint (MEX)
|
Use this option if you have an existing, in-process BizTalk WCF
endpoint whose metadata description you wish to expose on an IIS web
server. Because BizTalk receive locations are never "typed" to a
particular message schema, the in-process endpoints can't share any
metadata that explains their expected data contract. Using a MEX
endpoint, we can generate an IIS service that does nothing but expose
the contract of the in-process endpoint. We'll see more of this option
in the next section. |
I went ahead and chose Service endpoint with a WCF-WSHttp transport while also enabling the metadata endpoint and auto-generation of a receive location in the BizTalk application named BizTalkSOA.
After
selecting the desired service type, our next task is to identify which
artifacts the wizard should use to generate the WCF service. Our
current scenario will use the Publish BizTalk orchestrations as WCF service
option, which means that key service attributes like data contract,
communication pattern, and operation name are all retrieved from an
existing orchestration's logical port. On the next page of the wizard,
we are asked to designate which .NET assembly contains the
orchestration that we'd like to use to generate the service. If we
launched this wizard from within Visual Studio.NET when the BizTalk
project is open, then the assembly's file path will be automatically
populated by the wizard.
If the orchestration was successfully loaded by the wizard, then we
should see the (public) orchestration ports available for selection.
After accepting the current selected items, we enter a namespace for
the service. In this case, I supplied the namespace http://Seroter.BizTalkSOA.Chapter3/Service
.
Finally,
we need to specify the web server and address where our shiny new
service will be deployed. Once we've picked a valid URL, we see a
read-only view of the pending WCF service configuration. If everything
looks good, then finish the wizard and wait for confirmation that all
the necessary actions succeeded.
Configuring the Generated Components
We told the BizTalk WCF Service Publishing Wizard to build a receive location for us, so we should investigate exactly what we got as a result of that choice. In opening the BizTalk Administration Console and probing the BizTalkSOA
application, we should see a new receive location with a staggeringly
long name. It's hard to miss. Like all auto-generated messaging ports,
this receive location is in a Disabled state. The next step is to enable this receive location, and in the Orchestrations section of the application, both bind and start the deployed orchestration.
Double-clicking this new receive location reveals that it has been set up using the WCF-WSHttp
adapter and the BizTalk isolated host. However, the real meat of this
endpoint lies in the adapter configuration. We inspect this by clicking
the Configure
button next to the chosen adapter. The standard (non-custom) WCF
adapters all have a similar look and feel to their configuration pages
with the same set of tabs along the top. Here, we see the first
critical piece of the endpoint: the address.
The second tab in this receive location configuration, named Binding, shows a subset of available WCF-WSHttp
binding switches that we may alter for the adapter. Take note of the
fact that while the default number of concurrent calls for a WCF
service is 16, for performance reasons, the BizTalk WCF endpoint sets
this value to 200. If we desire deeper control over this binding
configuration, or wish to affix service behavior extensions, then the WCF-CustomIsolated adapter should be used instead.
The third tab is named Security
and provides us with a fairly straightforward way to apply standard WCF
security schemes to our endpoint. We'll stick with the default Message security mode shown here.
Finally, the last tab, named Messages,
is where we tell BizTalk where to extract the message body from the WCF
payload. We could send the entire SOAP payload to the BizTalk
MessageBox (SOAP envelope included), or even rip out a specific node in
the SOAP body. In our case, we just need the message body.
You may have noticed that nowhere in this receive location do we designate the C in the ABCs of WCF. Where is the contract?
All BizTalk receive locations are type-less, and the WCF receive
locations are no different. A BizTalk WCF endpoint exposes a generic
contract that will accept any valid SOAP message. This is why the
generation of a metadata endpoint is critical since otherwise we'd have
no clear way to tell consumers how to structure the payload their
service requests.
If you recall, our preceding run through the BizTalk WCF Service Publishing Wizard
produced this receive location and an actual WCF service hosted in IIS
7.0. If we had tried to visit the service in our web browser
immediately after the wizard had completed, we would have seen an error
telling us that the receive location was offline. Note that the receive
location for the associated WCF service must be enabled in order to
browse the service. Once the receive location gets enabled, we should
see a standard WCF service metadata page instead of an exception.
Let's
take advantage of this metadata endpoint by referencing and consuming
our new WCF service. In a new Visual Studio.NET console application we
choose to Add Service Reference
and point to the generated WCF service WSDL. Only a small bit of code
is needed to consume this simple service. However, note the unwieldy
type names that the orchestration-generated WCF service imparts upon us.
static void Main(string[] args)
{
OrchSvc.Seroter_BizTalkSOA_Chapter3_OrderManagement_BizTalk_Orch_ManageNewOrder_ReceiveNewOrderPortClient client =
new OrchSvc.Seroter_BizTalkSOA_Chapter3_OrderManagement_BizTalk_Orch_ManageNewOrder_ReceiveNewOrderPortClient("WSHttpBinding_ITwoWayAsync");
try
{
OrchSvc.NewOrder order = new OrchSvc.NewOrder();
order.OrderID = "123";
order.OrganizationID = "987";
order.PhysicianID = "555";
order.Status = "submitted";
Console.WriteLine("Calling WCF service ...");
client.SubmitNewOrder(ref order);
Console.WriteLine("Result status is: " + order.Status);
client.Close();
Console.ReadLine();
}
catch (System.ServiceModel.CommunicationException) { client.Abort(); }
catch (System.TimeoutException) { client.Abort(); }
catch (System.Exception) { client.Abort(); throw; }
}
At
this point, we've designed an orchestration, exposed its port interface
as a WCF service, and consumed that service from a WCF client.
Anatomy of a generated WCF WSDL
Attributes
set while building a BizTalk project seep into the WSDL of a generated
WCF service. Here is a quick look at which design-time properties map
to runtime WSDL attributes.
BizTalk project attribute
|
WCF WSDL attribute
|
---|
Value set for the Service Namespace in the WCF Service Publishing Wizard.
|
<wsdl:definitions
name="BizTalkServiceInstance" targetNamespace="http://Seroter.BizTalkSOA.Chapter3/Service"
xmlns:tns="http://Seroter.BizTalkSOA.Chapter3/Service">
|
Value set for the Service Namespace in the WCF Service Publishing Wizard.
|
<xsd:schema targetNamespace="http://Seroter.BizTalkSOA.Chapter3/Service/Imports">
|
Value set for Target Namespace in the BizTalk Schema.
|
<xsd:import schemaLocation="..." namespace="http://Seroter.BizTalkSOA.Chapter3.OrderManagement.BizTalk/Contract" />
|
Value of the orchestration's Namespace Typename of the orchestration, the name of the logical port being exposed and the name of the port Operation.
attribute combined with the |
<wsdl:message
name="Seroter_BizTalkSOA_Chapter3_OrderManagement_BizTalk_Orch_ManageNewOrder_ReceiveNewOrderPort_SubmitNewOrder_InputMessage">
|
BizTalk project attribute
|
WCF WSDL attribute
|
Value of the orchestration's Namespace Typename of the orchestration and the name of the logical port being exposed.
attribute combined with the |
<wsdl:portType name="Seroter_BizTalkSOA_Chapter3_OrderManagement_BizTalk_Orch_ManageNewOrder_ReceiveNewOrderPort">
|
Value of the orchestration's Namespace Typename of the orchestration and the name of the logical port being exposed.
Operation attribute combined with the
Value of the exposed orchestration port's name.
|
<wsdl:binding
name="WSHttpBinding_ITwoWayAsync"
type="tns:Seroter_BizTalkSOA_Chapter3_OrderManagement_BizTalk_Orch_ManageNewOrder_ReceiveNewOrderPort">
<wsdl:operation name="SubmitNewOrder">
|